package io.kiki.stack.http.feign;

import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

final class ReflectiveFeignParseHandlersByName<C> {

    private final Contract contract;
    private final MethodHandlerFactory<C> methodHandlerFactory;

    ReflectiveFeignParseHandlersByName(Contract contract, MethodHandlerFactory<C> methodHandlerFactory) {
        this.contract = contract;
        this.methodHandlerFactory = methodHandlerFactory;
    }

    public Map<Method, MethodHandler> apply(Target target, C requestContext) {
        final Map<Method, MethodHandler> result = new LinkedHashMap<>();

        final List<MethodMetadata> methodMetadataList = contract.parseAndValidateMetadata(target.type());
        for (MethodMetadata methodMetadata : methodMetadataList) {
            final Method method = methodMetadata.method();
            if (method.getDeclaringClass() == Object.class) {
                continue;
            }

            final MethodHandler methodHandler = createMethodHandler(target, methodMetadata, requestContext);
            result.put(method, methodHandler);
        }

        for (Method method : target.type().getMethods()) {
            if (Util.isDefault(method)) {
                final MethodHandler handler = new DefaultMethodHandler(method);
                result.put(method, handler);
            }
        }

        return result;
    }

    private MethodHandler createMethodHandler(final Target<?> target, final MethodMetadata methodMetadata, final C requestContext) {
        if (methodMetadata.isIgnored()) {
            return args -> {
                throw new IllegalStateException(methodMetadata.configKey() + " is not a method handled by feign");
            };
        }

        return methodHandlerFactory.create(target, methodMetadata, requestContext);
    }
}
